package com.photoeditor.demo.model.image.gl;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Rect;

import com.common.base.ComponentContext;
import com.photoeditor.demo.app.AppApplication;
import com.photoeditor.demo.model.image.util.Utils;
import com.photoeditor.demo.ui.widget.image.PreViewBitmap;
import com.photoeditor.demo.util.BitmapDecoder;
import com.photoeditor.demo.util.FetcherHolder;

import java.util.LinkedHashMap;
import java.util.Set;

/**
 * 
 * 用于预加载Decoder
 *
 */
public class DecoderLoader {
	
	protected LinkedHashMap<String, DecoderJobCache> mRunnables;
	
	protected int RUNNABLE_SIZE = 4;
	
	protected Object mRunnableLockObject = new Object();
	
	private ThreadPool mThreadPool = null;
	
	protected LinkedHashMap<String, DecoderJobCache> mImageViewRunnables;
	
	protected int IMAGEVIEW_RUNNABLE_SIZE = 3;
	
	public DecoderLoader(){
		initHashMap();
	}
	
	private void initHashMap(){
		mThreadPool = ((AppApplication) ComponentContext.getContext()).getThreadPool();
		synchronized (mRunnableLockObject) {
			mRunnables = new LinkedHashMap<String, DecoderJobCache>(RUNNABLE_SIZE, 1, false){
				/**
				 * 
				 */
				private static final long serialVersionUID = 1L;
	
				@Override
				public DecoderJobCache put(String key, DecoderJobCache value) {
					return super.put(key, value);
				}
				@Override
				protected boolean removeEldestEntry(
						Entry<String, DecoderJobCache> eldest) {
					synchronized (mRunnableLockObject) {
						if(size() > RUNNABLE_SIZE){//如果任务
							Future<?> task = eldest.getValue().task;
							if(task != null){
								task.cancel();
							}
							return true;
						}
						return super.removeEldestEntry(eldest);
					}
				}
			};

			mImageViewRunnables = new LinkedHashMap<String, DecoderJobCache>(IMAGEVIEW_RUNNABLE_SIZE, 1, false){
				/**
				 *
				 */
				private static final long serialVersionUID = 2L;

				@Override
				public DecoderJobCache put(String key, DecoderJobCache value) {
					return super.put(key, value);
				}

				@Override
				protected boolean removeEldestEntry(
						Entry<String, DecoderJobCache> eldest) {
					synchronized (mRunnableLockObject) {
						if(size() > IMAGEVIEW_RUNNABLE_SIZE){//如果任务
							Future<?> task = eldest.getValue().task;
							if(task != null){
								task.cancel();
							}
							return true;
						}
						return super.removeEldestEntry(eldest);
					}
				}
			};
		}
	}
	
	public void removeSameRunnable(String key){
		synchronized (mRunnableLockObject) {
			DecoderJobCache cache = mRunnables.remove(key);
			if(cache != null && cache.task != null){
				cache.task.cancel();
			}
		}
	}
	
	/**
	 * 使用同一个线程池去load decoder
	 * @param path
	 */
	public void loadDecoder(String path){
		synchronized (mRunnableLockObject) {
			if(FetcherHolder.getPreviewBitmapCache().getDecoderFormCache(path) == null){//缓存中不存在
				DecoderJobCache cache = mImageViewRunnables.get(path);
				if(cache == null){
					mRunnables.put(path, new DecoderJobCache(new DecodeDecoderJob(path)));
				}
			}
		}
	}
	
	/**
	 * 使用同一个线程池去load decoder
	 * 为PreViewBitmap loadDecoder和图片
	 */
	public Future<BitmapRegionDecoder> loadDecoderAndBitmap(PreViewBitmap.PreViewBitmapJob<BitmapRegionDecoder> job, FutureListener<BitmapRegionDecoder> listener){
		DecoderJobCache cache = new DecoderJobCache(job, listener);
		Future<BitmapRegionDecoder> task = cache.excute();
		synchronized(mRunnableLockObject){
			removeSameRunnable(cache.job.path);
			mImageViewRunnables.put(cache.job.path, cache);
		}
		return task;
	}
	
	public class DecoderJobCache{
		PreViewBitmap.PreViewBitmapJob<BitmapRegionDecoder> job;
		Future<BitmapRegionDecoder> task;
		
		/**
		 * listener 为空的只需要解析decoder
		 */
		FutureListener<BitmapRegionDecoder> listener;
		
		public DecoderJobCache(PreViewBitmap.PreViewBitmapJob<BitmapRegionDecoder> job){
			this(job, null);
		}
		
		public DecoderJobCache(PreViewBitmap.PreViewBitmapJob<BitmapRegionDecoder> job, FutureListener<BitmapRegionDecoder> listener){
			this.job = job;
			this.listener = listener;
			if(this.listener == null){
				this.listener = createOwnerFutureListener();
			}
		}
		
		public Future<BitmapRegionDecoder> excute(){
			if(job != null){
				task = mThreadPool.submit(job, new FutureListener<BitmapRegionDecoder>() {
					
					@Override
					public void onFutureDone(Future<BitmapRegionDecoder> future) {//完成decode
						if(listener != null){
							if(listener instanceof DecodeBitmapFutureListener){
								listener.onFutureDone(future);
								synchronized(mRunnableLockObject){
									mRunnables.remove(job.path);
									if(mImageViewRunnables.size() < IMAGEVIEW_RUNNABLE_SIZE){
										DecoderJobCache myJob = getNewestRunnable();
										if(myJob != null){
											myJob.excute();
										}
									}
								}
							} else{
								listener.onFutureDone(future);
								synchronized(mRunnableLockObject){
									mImageViewRunnables.remove(job.path);
									DecoderJobCache cache = mRunnables.remove(job.path);
									if(mImageViewRunnables.size() < IMAGEVIEW_RUNNABLE_SIZE){
										DecoderJobCache myJob = getNewestRunnable();
										if(myJob != null){
											myJob.excute();
										}
									}
									
									/**
									 * job.path 路径的已经执行完成 用于移除一样的Task
									 */
									if(cache != null){
										if(cache.task != null){
											cache.task.cancel();
										}
									}
								}
							}
						}
					}
				});
			}
			return task;
		}
		
		public DecodeBitmapFutureListener createOwnerFutureListener(){
			return new DecodeBitmapFutureListener(job.path);
		}
	}
	
	protected DecoderJobCache getNewestRunnable(){
		Set<String> set = mRunnables.keySet();
		int size = set.size();
		if(size == 0) return null;
		int i = 0;
		for(String str : set){
			i++;
			if(i == size){
				return mRunnables.get(str);
			}
		}
		return null;
	}
	
	private class DecodeDecoderJob extends PreViewBitmap.PreViewBitmapJob<BitmapRegionDecoder> {
		public DecodeDecoderJob(String str){
			path = str;
		}

		@Override
		public BitmapRegionDecoder run(ThreadPool.JobContext jc) {
			jc.setMode(ThreadPool.MODE_NONE);
			BitmapRegionDecoder decoder = FetcherHolder.getPreviewBitmapCache().getDecoderFormCache(path);
			if(decoder == null || decoder.isRecycled()){
				try {
					{
						if(BitmapDecoder.isVaildImage(path)){
							decoder = BitmapRegionDecoder.newInstance(path, false);
						}
					}
				} catch (Throwable e) {
					e.printStackTrace();
				}
			}
			FetcherHolder.getPreviewBitmapCache().addDecoderToCache(path, decoder);
			return decoder;
		}
	}
	
	public class DecodeBitmapFutureListener implements FutureListener<BitmapRegionDecoder>{
		private String path;
		
		public DecodeBitmapFutureListener(String path){
			this.path = path;
		}
		
		@Override
		public void onFutureDone(Future<BitmapRegionDecoder> future) {
			BitmapRegionDecoder decoder = future.get();
			if (decoder == null || decoder.isRecycled()){
				return;
			}
            int width = decoder.getWidth();
            int height = decoder.getHeight();
            Bitmap bitmap = FetcherHolder.getPreviewBitmapCache().getBitmapFromCache(path);
            if(bitmap == null){
	            BitmapFactory.Options options = new BitmapFactory.Options();
	            options.inSampleSize = Utils.computeSampleSize(Utils.TARGET_SIZE * 1.0f / Math.max(width, height));
	            int duration = 0;
	            long time = System.currentTimeMillis();
	            bitmap = decoder.decodeRegion(new Rect(0, 0, width, height), options);
				if(bitmap == null){
					bitmap = BitmapFactory.decodeFile(path, options);
				}
	            duration = (int)(System.currentTimeMillis() - time);
	            FetcherHolder.getPreviewBitmapCache().addBitmapToCache(path, bitmap, true, duration > 100);
            } else{
            	FetcherHolder.getPreviewBitmapCache().addBitmapToCache(path, bitmap, false, true);
            }
		}
	}
	
	
	public void recycle(){
		synchronized(mRunnableLockObject){
			if(mRunnables != null){
				Set<String> set = mRunnables.keySet();
				for(String path : set){
					Future<BitmapRegionDecoder> task = mRunnables.get(path).task;
					if(task != null){
						task.cancel();
					}
				}
				mRunnables.clear();
			}
			
			if(mImageViewRunnables != null){
				Set<String> set = mImageViewRunnables.keySet();
				for(String path : set){
					Future<BitmapRegionDecoder> task = mImageViewRunnables.get(path).task;
					if(task != null){
						task.cancel();
					}
				}
				mImageViewRunnables.clear();
			}
		}
	}
}
